import re
import time
import tkinter as tk


# 第一次排序，根据匹配的下标进行排序
def complete_data_sort_index(data):
    return data[0]
# 第二次排序，根据字母表顺序排序
def complete_data_sort_str(data):
    s = data[1]
    return s

# 单行输入框_可自动补全，需要有识别参数才行
class EntryInListbox(tk.Entry):
    def __init__(self, master=None,
                 complete_list:list[str]=None,  # 总字符串列表
                 layout='pack',             # 布局方式
                 pack_kwargs:dict =None,    # pack布局参数
                 grid_kwargs:dict = None,
                 **kwargs):
        super().__init__(master, **kwargs)
        if complete_list is None:
            complete_list = []
        if pack_kwargs is None:     # 参数预处理
            pack_kwargs = {}
        if grid_kwargs is None:
            grid_kwargs = {}
        self.layout = layout  # 布局方式与参数
        self.pack_kwargs = pack_kwargs
        self.grid_kwargs = grid_kwargs

        self.complete_list = complete_list
        self.sub_complete_list = []     # 匹配得到的字符串列表

        # 绑定entry事件
        self.bind('<KeyRelease>', self.show_listbox)
        self.bind("<Down>", self._on_down)
        self.bind("<Up>", self._on_up)
        self.bind("<Return>", self._on_return)
        self.bind("<FocusOut>", self._on_focus_out)


        self.listbox = tk.Listbox(height=5)     # 与self代表的entry是同级界面
        self.listbox_index = 0
        self.listbox.bind("<Double-Button-1>", self._on_listbox_select)  # 双击左键进行选择
        # 暂时初始化list_box
        # self.listbox_init()
        # 自适应展示
        self.widget_layout_show(self)

    # def listbox_init(self):
    #     for i in self.complete_list:
    #         self.listbox.insert('end', i)
    def _on_focus_out(self, *args):
        # if self.listbox.focus_get()
        if not self.listbox.focus_get() is self.listbox:
            self.listbox.place_forget()

    def _on_return(self,*args):
        if self.listbox.winfo_ismapped():
            # 获取选中项的值
            selection_value = self.listbox.get(self.listbox.curselection())
            self.delete(0, tk.END)
            self.insert(0, selection_value)
            print('跑不跑')
            self.listbox.place_forget()    # 隐藏列表
            return "break"  # 阻止默认的Tab行为

    def _on_down(self,*args):
        if self.listbox.winfo_ismapped():   # 如果该组件存在
            if self.listbox_index < len(self.sub_complete_list)-1:
                self.listbox_index += 1
            self.listbox.selection_set(self.listbox_index)

    def _on_up(self,*args):
        if self.listbox.winfo_ismapped():   # 如果该组件存在
            if self.listbox_index > 0:
                self.listbox_index -= 1
            self.listbox.selection_set(self.listbox_index)

    def _on_listbox_select(self,*args):
        selection = self.listbox.get(self.listbox.curselection())
        self.delete(0, tk.END)
        self.insert(0, selection)
        self.listbox.place_forget()
        self.focus_set()

    # 自动补全已经完成
    def show_listbox(self,e):
        print(e.__dict__)
        if e.keycode != 38 and e.keycode != 40: # 按下的是↑或者↓键，不会重置下标
            self.listbox_index = 0
        if e.keycode == 13:     # 如果按下 回车键，则无反应。
            return
        print('111111')
        self.listbox.delete(0,'end')
        text = self.get()       # 文本框中，有文字才显示listbox进行补全
        if not text:
            self.listbox.place_forget()
            return

        # 编译正则表达式
        comp = re.compile(text)
        datas = []
        for complete in self.complete_list:
            res_comp = comp.search(complete)       # 获取匹配结果
            if res_comp:    # 收集匹配成功的首位下标，和对应的字符串
                datas.append((res_comp.span()[0], complete))
        datas.sort(key=complete_data_sort_index)    # 下标排序
        datas.sort(key=complete_data_sort_str)      # 字母排序
        self.sub_complete_list = list(map(lambda x:x[1], datas)) # 使用map函数提取出，单列数组
        # 插入数据
        for i in self.sub_complete_list:
            self.listbox.insert('end', i)

        # 显示列表框
        x = self.winfo_x()
        y = self.winfo_y() + self.winfo_height()
        width = self.winfo_width()
        print(x, y, width)
        # listbox.place(x=0,y=0)
        self.listbox.place(x=x, y=y, width=width)
        self.listbox.selection_set(self.listbox_index)   # 选择第一项



    # 自适应布局底层的方法
    def widget_layout_show(self,widget:tk.Widget=None):
        if self.layout in ('p','P','pack','PACK'):
            widget.pack(self.pack_kwargs)
        elif self.layout in ('g','G','grid','GRID'):
            widget.grid(self.grid_kwargs)

    def widget_layout_forget(self,widget:tk.Widget=None):
        if self.layout in ('p','P','pack','PACK'):
            widget.pack_forget()
        elif self.layout in ('g','G','grid','GRID'):
            widget.grid_forget()

if __name__ == '__main__':
    l = ['assert','abcd', 'asvvv', 'asszert','asvaed']
    root = tk.Tk()
    root.geometry('200x200+50+50')
    test_widget = EntryInListbox(root, complete_list=l)
    root.mainloop()



